react 您所在的位置:网站首页 react 源码学习 react

react

#react| 来源: 网络整理| 查看: 265

what is react-helmet?

是一个用来管理document.head的一个react component;支持服务端渲染

源码 Helmet.js代码框架 // 导入依赖 ... import withSideEffect from "react-side-effect"; ... // 高阶组件 const Helmet = Component => class HelmetWrapper extends React.Component { // logic code } const NullComponent = () => null; const HelmetSideEffects = withSideEffect( reducePropsToState, handleClientStateChange, mapStateOnServer )(NullComponent); const HelmetExport = Helmet(HelmetSideEffects); HelmetExport.renderStatic = HelmetExport.rewind; // export export {HelmetExport as Helmet}; export default HelmetExport; 看到了高阶组件,那就从这个开始吧 const Helmet = Component => class HelmetWrapper extends React.Component { static propTypes = { // 属性类型检查 title: PropTypes.string, ... } // 默认属性值 static defaultProps = { //... } // 测试使用 忽略它 static peek = Component.peek; // 没写注释 先略过 static rewind = () => { } // 看命名猜测是和环境相关 static set canUseDOM(canUseDOM) { Component.canUseDOM = canUseDOM; } // 使用深比较判断组件是否需要更新 // isEqual 使用了react-fast-compare这个库,稍后看 shouldComponentUpdate(nextProps) { return !isEqual(this.props, nextProps); } // 看命名是和处理子组件 title meta等相关,先跳过 mapNestedChildrenToProps(child, nestedChildren) { //... } // 跳过 flattenArrayTypeChildren() { } // 跳过 mapObjectTypeChildren() {} ... //直接看render 其他的在调用的时候再看 render() { // children fiberNode子节点 // props 其他的属性 const {children, ...props} = this.props; let newProps = {...props}; if (children) { // 看到了这里 newProps = this.mapChildrenToProps(children, newProps); } return ; } } this.mapChildrenToProps

最终经过这个函数得到了一个对象, 传递给了Component,Component就是HelmetSideEffects。

image.png

HelmetSideEffects如何被创建的 withSideEffect( reducePropsToState, handleClientStateChange, mapStateOnServer )(NullComponent); withSideEffect

所以关键就是 withSideEffect的功能了;它来自这个包react-side-effect

withSideEffect( reducePropsToState, handleStateChangeOnClient, mapStateOnServer,// 可选 )(YourComonent);

这个高阶组件的作用是:

YourComonent 可以多次,也可以嵌套的使用(内部维护了一个实例数组) withSideEffect能够监听所有YourComonent willMount willUnMount componentDidUpdate的触发,按照嵌套规则收集YourComponent上的props到一个propsList数组中 然后高阶组件的第一个参数reducePropsToState 你可以拿到propsList,去做自己的业务逻辑处理 function reducePropsToState(propsList) { // logic code // maybe return last props.title // return propsList[propsList.length -1].title } reducePropsToState每次执行之后,都会执行handleStateChangeOnClient,这个方法用来在客户端执行相应的副作用操作,比如修改document.title mapStateOnServer是用于将reducePropsToState返回的state,再进行数据处理,以便在服务端渲染的时候进行使用

处理title的一个简单实现,具体查看这个关于document.title的处理react-document-title

下面决定看下 react-side-effect的实现

withSideEffect的实现 export default function withSideEffect( reducePropsToState, handleStateChangeOnClient, mapStateOnServer ) { // 参数校验 跳过 //... // 设置displayName 跳过 // 返回一个高阶组件 return function wrap(WrappedComponent) { if (typeof WrappedComponent !== 'function') { throw new Error('Expected WrappedComponent to be a React component.'); } // 维护了所有你传入的组件的实例对象,方便在服务端渲染的时候使用 let mountedInstances = []; // 状态 handleStateChangeOnClient or mapStateOnServer的入参 let state; // 触发整个更新过程 reducePropsToState-> newState -> handleStateChangeOnClient|mapStateOnServer function emitChange() { state = reducePropsToState(mountedInstances.map(function (instance) { return instance.props; })); if (SideEffect.canUseDOM) { // 客户端执行回调 handleStateChangeOnClient(state); } else if (mapStateOnServer) { // 服务端渲染执行执行逻辑 state = mapStateOnServer(state); } } // 内部类,我们在代码中引用的组件其实是这个 class SideEffect extends PureComponent { // Try to use displayName of wrapped component static displayName = `SideEffect(${getDisplayName(WrappedComponent)})`; // Expose canUseDOM so tests can monkeypatch it static canUseDOM = canUseDOM; // 获取state static peek() { return state; } // 服务端渲染的时候 获取到当前的state,并且重置了一些内部状态 static rewind() { if (SideEffect.canUseDOM) { throw new Error('You may only call rewind() on the server. Call peek() to read the current state.'); } let recordedState = state; state = undefined; mountedInstances = []; return recordedState; } // willMount的时候触发一次emitChange UNSAFE_componentWillMount() { // 每一个组件挂载之前 都添加到mountedInstances 闭包变量中 mountedInstances.push(this); emitChange(); } // didUpdate的时候触发一次emitChange componentDidUpdate() { emitChange(); } // willUnmount的时候触发一次emitChange componentWillUnmount() { const index = mountedInstances.indexOf(this); mountedInstances.splice(index, 1); emitChange(); } render() { // 这里是你的组件 YourComponent return ; } } return SideEffect; } } 总结

回头梳理Helmet的代码,整个流程就清晰了(伪代码逻辑)。

//HelmetSideEffects 的逻辑 ---start //只负责设置header内的结构,不渲染其他 //故 hello --> 并不会渲染hello字符串 const NullComponent = () => null; // 使用withSideEffect // 这一步达到的效果是 HelmetSideEffects 实例的挂载、更新、卸载都会触发 // reducePropsToState-->handleClientStateChange|mapStateOnServer const HelmetSideEffects = withSideEffect( reducePropsToState, handleClientStateChange, mapStateOnServer )(NullComponent); //HelmetSideEffects 的逻辑 ---end // 导出的模块Helmet const HelmetExport = Helmet(HelmetSideEffects); const Helmet = (Component)=> class HelmetWrapper extends React.Component { render() { // 内部封装的所有逻辑在做这个事情,将react children(fiberNode)转换成了HelmetSideEffects的props // props.children-> newProps // return ; } } // 服务端渲染使用 获取最终的state HelmetExport.renderStatic = HelmetExport.rewind; // 我们使用的组件 Helmet -> ... export default HelmetExport;


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有